#!/usr/bin/env python2
# -*- coding: utf-8 -*-
"""
Python 2.7 隧道中继器
监听 24444/24445，解包后注入本地同名端口
"""
import socket, struct, select, threading

TCP_LPORT = 24444
UDP_LPORT = 24445

# ---------- 通用工具 ----------
def relay(sock1, sock2):
    while True:
        r, _, _ = select.select([sock1, sock2], [], [], 60)
        if sock1 in r:
            data = sock1.recv(4096)
            if not data: break
            sock2.sendall(data)
        if sock2 in r:
            data = sock2.recv(4096)
            if not data: break
            sock1.sendall(data)
    sock1.close(); sock2.close()

def recv_exact(sock, n):
    buf = b''
    while len(buf) < n:
        chunk = sock.recv(n - len(buf))
        if not chunk: raise socket.error("EOF")
        buf += chunk
    return buf

# ---------- TCP 解包 ----------
def tcp_relay_server():
    srv = socket.socket(); srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind(('0.0.0.0', TCP_LPORT)); srv.listen(10)
    while True:
        tunnel, addr = srv.accept()
        header = recv_exact(tunnel, 4)          # 2 字节端口 + 2 字节保留
        port = struct.unpack('!H', header[:2])[0]
        backend = socket.create_connection(('127.0.0.1', port))
        threading.Thread(target=relay, args=(tunnel, backend)).start()

# ---------- UDP 解包 ----------
def udp_relay_server():
    srv = socket.socket(); srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind(('0.0.0.0', UDP_LPORT)); srv.listen(10)
    while True:
        tunnel, addr = srv.accept()
        def handler():
            udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            while True:
                header = recv_exact(tunnel, 4)
                port, dlen = struct.unpack('!HH', header)
                data = recv_exact(tunnel, dlen)
                udp.sendto(data, ('127.0.0.1', port))
                # 回包
                udp.settimeout(2)
                try:
                    reply, _ = udp.recvfrom(65535)
                    tunnel.sendall(struct.pack('!HH', port, len(reply)) + reply)
                except socket.timeout:
                    pass
        threading.Thread(target=handler).start()

# ---------- 主入口 ----------
def main():
    threading.Thread(target=tcp_relay_server).start()
    threading.Thread(target=udp_relay_server).start()
    print('[*] 中继器已启动，监听 24444/24445')
    #python3
    while True:
        input('')
    #python2
    #while True: raw_input('')
if __name__ == '__main__':
    main()